import java.util.concurrent.locks.Lock;

import agentFramework.agent.Agent;
import agentFramework.core.Core;
import agentFramework.core.Message;
import agentFramework.core.env.Environment;
import agentFramework.tools.LeaderDetector;
import agentFramework.tools.LogData;
import agentFramework.utils.Debug;


public class IrishCore extends Core
{
	public final static int		STATE_MANIFEST_INTENTIONS = 0;
	public final static int		STATE_PROCESS_QUEUE = 1;
	public final static int		STATE_REVISE_STRATEGY = 2;
	
	public int 					ITERATIONS;
	public int					EPOCHS;
	
	public int					globalCounter;
	public int					currentState;
	public String				stateMsgContent;
	public int					nEpochs;
	public int					nIterations;
	
	public LeaderDetector		leaderDetector;
	
	
	// constructor
	public IrishCore(Environment env, int iterations, int epochs) 
	{
		super(env);
		
		this.globalCounter = -1;
		this.nEpochs = 0;
		this.nIterations = 0;
		this.currentState = STATE_MANIFEST_INTENTIONS;
		this.stateMsgContent = Constants.MSG_CONTENT_MANIFEST_INTENTION;
		this.leaderDetector = null;
		this.ITERATIONS = iterations;
		this.EPOCHS = epochs;
	}

	
	public void setLeaderDetector(LeaderDetector ld)
	{
		this.leaderDetector = ld;
	}


	public void changeState()
	{
		switch(currentState)
		{
			case STATE_MANIFEST_INTENTIONS:
			{
				currentState = STATE_PROCESS_QUEUE;
				stateMsgContent = Constants.MSG_CONTENT_PROCESS_REMAINING_MESSAGES;
				
				break;
			}
			
			case STATE_PROCESS_QUEUE:
			{
				if (nIterations == ITERATIONS)
				{
					currentState = STATE_REVISE_STRATEGY;
					stateMsgContent = Constants.MSG_CONTENT_REVISE_STRATEGY;
					nIterations = 0;
				}	
				else
				{
					nIterations++;
					currentState = STATE_MANIFEST_INTENTIONS;
					stateMsgContent = Constants.MSG_CONTENT_MANIFEST_INTENTION;
				}
				
				break;
			}

			case STATE_REVISE_STRATEGY:
			{
				summarizeActions();
				
				nEpochs++;
				currentState = STATE_MANIFEST_INTENTIONS;
				stateMsgContent = Constants.MSG_CONTENT_MANIFEST_INTENTION;
				
				System.out.println("epoch " + (nEpochs - 1) + " ended");
				stepStatus = Core.SIM_WAIT;
				epochLock.unlock();
				stepLock.lock();
				stepLock.unlock();
				epochLock.lock();
				stepStatus = Core.SIM_RUNNING;

				break;
			}
			
			default: break;
		}
		
		Debug.print("CORE: state changed to: " + stateMsgContent);
	}
	
	
	public void broadcastMessage(Message msg)
	{
		Debug.print("CORE: broadcast msg: \n" + msg.toString());
		
		for (Agent a: environment.getAgentList())
		{
			msg.receiverID = a.getAgentId();
			sendMesage(msg);
		}
	}
	
	
	@Override
	public void handleMessage(Message msg) 
	{	
		if (Constants.MSG_CONTENT_DONE.equals(msg.content))
		{
			Debug.print("CORE: message received: \n" + msg.toString());
			globalCounter++;
		}
	}

	
	@Override
	public void behavior() 
	{
		epochLock.lock();
		int i = 0;
		Message taskMsg = new Message(this.id, 0, Message.TYPE_ADMINISTRATIVE, stateMsgContent); 

		environment.getMessageDispatcher().start();
		
		Debug.print("CORE: starting the agents");

		for (Agent a: environment.getAgentList())
		{
			i++;
			a.start();
		}
		System.out.println("agents : " + i);
		Debug.print("CORE: start sending administrative messages");
		
		while(true)
		{
			if (nEpochs == EPOCHS)
			{
				break;
			}
			
			
			if (globalCounter == -1)
			{
				globalCounter = 0;
				broadcastMessage(taskMsg);
			}
			
			lock.lock();
			
			while (!messageQueue.isEmpty())
			{
				Message msg = messageQueue.pollFirst();
				
				lock.unlock();
				handleMessage(msg);
				lock.lock();
			}
			
			lock.unlock();
			
			if (globalCounter == environment.getAgentCount())
			{
				Debug.print("CORE: Tasks accomplished by all the agents");
				
				globalCounter = -1;
				changeState();
				taskMsg.content = stateMsgContent;
			}
		}
		
		epochLock.unlock();
		 
		Debug.print("CORE: stop the agents");

		for (Agent a: environment.getAgentList())
		{
			a.stop();
		}

		try
		{	
			for (Agent a: environment.getAgentList())
			{
				a.join();
			}
		}
		catch(Exception e) { e.printStackTrace(); }
	}
	
	
	public void summarizeActions()
	{
		LogData logData = new LogData(nEpochs);

		for (Agent a : environment.getAgentList())
		{
			logData.addAction(a.getAgentId(), a.getCurrentAction());
		}
		
		leaderDetector.addLogData(logData);
	}
	
}
